'use strict';

var W = W || {};
W.App = W.App || {};
W.App.name = 'Templates';
var EDIT_TEMPLATE_EVENT_ID = 15;
W.App.Templates = {
  name: 'plus',
  getReferrerUrl: function getReferrerUrl() {
    return encodeURIComponent(document.referrer);
  },
  getLocationUrl: function getLocationUrl() {
    return encodeURIComponent(document.location.href);
  },
  updateCurrentView: function updateCurrentView() {
    // get the current view from #currentView object (by server)
    // and set it on W.App.Templates.currentView
    var biDataElement = document.getElementsByTagName("bi-page-impression-data")[0];

    if (biDataElement) {
      W.App.Templates.currentView = JSON.parse(biDataElement.innerHTML);

      // remove #currentView element from DOM
      biDataElement.parentNode.removeChild(biDataElement);
    } else {
      W.App.Templates.currentView = {};
    }
  },
  getEventData: function getEventData() {
    if (!W.App.Templates.currentView) {
      W.App.Templates.updateCurrentView();
    }

    return encodeURIComponent(JSON.stringify(W.App.Templates.currentView));
  },
  infoShowGate: function (e, props, complete) {
    if (this.parentElement.firstElementChild.checked) {
      props.evid = 25;
    }

    complete();
  },
  infoHideGate: function (e, props, complete) {
    if (e.type == 'mouseleave') {
      if (this.parentElement.firstElementChild.checked) {
        complete();
      }
    } else {
      if (this.parentElement.firstElementChild.checked) {
        props.evid = 25;
      }

      complete();
    }
  },
  hoverEventGate: function (e, props, complete) {
    if (e.target == this && this.classList.contains('-active')) {
      this.classList.remove('-active');
      complete();
    }
  },
  clickThumbnailMeansView: function (e, props, complete) {
    if (W.Globals.Experiments.clickThumbnailMeansView && W.App.Templates.thumbnailClickGate(e)) complete();
  },
  thumbnailClickGate: function (e) {
    var blockers  = ['tg-button', 'tg-info-link', 'tg-information', 'tg-hover-toggle'],
        eventPath = e.path;

    if (!eventPath) {
      var eventPath = [e.target],
          node = e.target;

      do {
        node = node.parentNode || window;
        eventPath.push(node);
      } while (node != window);
    }

    eventPath = eventPath.reduce(function (eventPath, el) {
      return eventPath + (eventPath.indexOf('tg-thumbnail') == -1 ? el.className + ' ' : '');
    }, '');

    return !(blockers.some(function (blocker) {
      return eventPath.indexOf(blocker) > -1;
    }));
  }
};
W.App.Templates.BI = {
  extension: 'spettatore_bi',
  defaultProperties: {
    src: 2,
    siteType: 2,
    galleryDocsPerPage: 12,
    book: W.Bi.BOOK
  }
};

(function (window, document, undefined) {
  'use strict';

  var isOptiscrollCompatible = true;

  function randomUUID() {
    var s = [], itoh = '0123456789ABCDEF';
    for (var i = 0; i < 36; i++) s[i] = Math.floor(Math.random() * 0x10);
    s[14] = 4;
    s[19] = (s[19] & 0x3) | 0x8;
    for (var i = 0; i < 36; i++) s[i] = itoh[s[i]];
    s[8] = s[13] = s[18] = s[23] = '-';
    return s.join('');
  }

  function makeApiUrl(entryPoint) {
    var local = location.hostname == 'localhost';

    return window.location.origin + (local ? '/api/' : '/' + W.App.Templates.name + '/') + entryPoint;
  }

  function debounce(func, wait, immediate) {
    var timeout;

    return function () {
      var context = this, args = arguments;

      var later = function () {
        timeout = null;
        if (!immediate) func.apply(context, args);
      };

      var callNow = immediate && !timeout;

      clearTimeout(timeout);
      timeout = setTimeout(later, wait);

      if (callNow) func.apply(context, args);
    };
  };

  var delimiter   = '/html/',
      rootPath    = location.pathname.split(delimiter)[0],
      environment = window.location.hostname.substring(window.location.hostname.indexOf('.'));

  if (!window.location.origin) {
    window.location.origin = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port : '');
  }

  var forEach = Array.prototype.forEach,
      map     = Array.prototype.map;

  var main = document.getElementsByTagName('main')[0],
      app  = document.getElementById('app'),
      updateSidebar,
      headerHeight;

  var _infos, _thumbnails, _infoSwitches, _hovers;

  function resetElementRefs() {
    _infos        = null;
    _thumbnails   = null;
    _infoSwitches = null;
    _hovers       = null;
  }

  function getInfos() {
    if (!_infos) _infos =
      document.querySelectorAll('.tg-information');

    return _infos;
  }

  function getThumbnails() {
    if (!_thumbnails) _thumbnails =
      document.querySelectorAll('.tg-template-img');

    return _thumbnails;
  }

  function getInfoSwitches() {
    if (!_infoSwitches) _infoSwitches =
      document.querySelectorAll('.tg-hover > input[type=checkbox]');

    return _infoSwitches;
  }

  function getHovers() {
    if (!_hovers) _hovers =
      document.querySelectorAll('.tg-hover');

    return _hovers;
  }

  var errorObject = { value: null };

  function tryCatch(fn) {
    try {
      return fn();
    }
    catch (e) {
      errorObject.value = e;
      return errorObject;
    }
  }

  var wheelEvents = ('onwheel' in document || document.documentMode >= 9) ?
                    ['wheel'] : ['mousewheel', 'DomMouseScroll', 'MozMousePixelScroll'];

  // console.log('wheel events:', wheelEvents)
  var getScrollTop, setScrollTop;

  if (isOptiscrollCompatible) {
    getScrollTop = function () {
      return app.scrollTop;
    }

    setScrollTop = function (scrollTop) {
      app.scrollTop = scrollTop;
    }
  } else {
    getScrollTop = function () {
      return (isBlink || isSafari || isEdge) ? document.body.scrollTop
                                             : document.documentElement.scrollTop;
    }

    setScrollTop = function (scrollTop) {
      if (isBlink || isSafari || isEdge) {
        document.body.scrollTop = scrollTop;
      } else {
        document.documentElement.scrollTop = scrollTop;
      }
    }
  }

  W.App.Templates.newEditorSession = function newEditorSession() {
    W.App.Templates.lastEditorSession = randomUUID();

    return W.App.Templates.lastEditorSession;
  }

  W.App.Templates.constructEditorUrl = function constructEditorUrl(metaSiteId, siteId, editorSessionId) {
    var url = 'http://editor' + environment + '/html/editor/web/renderer/new';
    if (W.Globals.Experiments.enableTemplateVerticals) {
      url = 'http://www.wix.com/marketing-template-verticals'
    }

    url += '?siteId=' + siteId;

    if (metaSiteId && metaSiteId != 'none')
      url += "&metaSiteId=" + metaSiteId;

    if (editorSessionId)
      url += '&editorSessionId=' + editorSessionId;

    return url;
  }

  W.App.Templates.constructEditorUrlAsParam = function constructEditorUrlAsParam(metaSiteId, siteId) {
    return encodeURIComponent(W.App.Templates.constructEditorUrl.apply(W.App.Templates, arguments));
  }

  W.App.Templates.openInEditor = function openInEditor(metaSiteId, siteId) {
    Array.prototype.push.call(arguments, W.App.Templates.lastEditorSession);

    window.open(W.App.Templates.constructEditorUrl.apply(W.App.Templates, arguments), '_blank');
  }

  function declarativeBI(appName) {
    var app = W.App[appName];

    var onDomReady = function (callback) {
      document.readyState == 'interactive' || document.readyState == 'complete' ? callback() : document.addEventListener('DOMContentLoaded', callback);
    };

    var BI_PREFIX = 'bi-',
        LIVE_EVENT = true,
        EVENT_INTERCEPTOR = '$event';

    var events = [
      'click',
      'dblclick',
      'contextmenu',
      'mouseover',
      'mouseout',
      'mouseenter',
      'mouseleave',
      'mousedown',
      'mouseup',
      'mousemove',
      'keyup',
      'keydown',
      'keypress',
      'input',
      'focus',
      'focusin',
      'blur',
      'focusout',
      'change',
      'load',
      'resize',
      'select',
      'selectstart',
      'selectionchange',
      'transitionend',
      'cut',
      'copy',
      'paste'
    ];

    function shallowClone(obj) {
      var clone = {};

      for (var property in obj) {
        if (obj.hasOwnProperty(property)) clone[property] = obj[property];
      }

      return clone;
    }

    function stripBI(type) {
      return type.replace(BI_PREFIX, '');
    }

    function prefixBI(type) {
      return BI_PREFIX + type;
    }

    function getAllBIControls() {
      var biEvents = events
            .map(function (type) {
              return '[' + prefixBI(type) + ']';
            })
            .join(', ');

      return document.querySelectorAll(biEvents);
    }

    function parseBIProperyString(biPropertiesString) {
      return biPropertiesString
        .split(/;(?=[^"^']*(?:["|'][^"^']*["|'][^"^']*)*$)/) // http://stackoverflow.com/a/11620387/1049693
        .map(Function.call, String.prototype.trim)
        .reduce(function (biProperties, biRule) {
          biRule = biRule
            .split(/:(?=[^"^']*(?:["|'][^"^']*["|'][^"^']*)*$)/)
            .map(Function.call, String.prototype.trim);

          // If developer has provided a value for this rule:
          if (biRule[1]) {
            // Check if value is a URL
            var findUrl = /url(?:\('(.*)'\)|\("(.*)"\))/,
                url = findUrl.exec(biRule[1]);
            if (url) biRule[1] = encodeURIComponent(url[1]);
            else {
              var findFunction = /^(!?)(?!url\()(\w+)\(\s*([^)]+?)?\s*\)$/,
                  fnCall = findFunction.exec(biRule[1]);

              // Check if value is a function call
              if (fnCall) {
                var fnName = fnCall[2],
                    fnArgs = fnCall[3];

                fnArgs && (fnArgs = fnArgs
                                      .split(',')
                                      .map(Function.call, String.prototype.trim));

                var fn = app[fnName] || app.BI.defaultProperties[fnName];

                // Handle event interceptors
                if (biRule[0] == EVENT_INTERCEPTOR) {
                  biRule[1] = fn;
                // Handle functions that need to be called as the
                // bi event will take place
                } else if (fnCall[1] == '!') {
                  fnArgs && fnArgs.unshift(null);
                  biRule[1] = Function.bind.apply(fn, fnArgs);
                // This is a static function, invoke it now and cache the result
                } else {
                  biRule[1] = fn.apply(null, fnArgs);
                }

              }
            }

            biProperties[biRule[0]] = biRule[1];
          // If not, attempt to get it from defaults for this app
          } else {
            if (app.BI.defaultProperties[biRule[0]]) {
              biProperties[biRule[0]] = app.BI.defaultProperties[biRule[0]];
            // Finally if no value is provided, remove this rule
            } else {
              delete biProperties[biRule[0]];
            }
          }

          return biProperties;
        }, {});
    }

    function prepareBICallsign(biProperties, shouldAcceptInterceptors) {
      var fn,
          finalProperties = {
            callsign: {}
          };

      if (typeof shouldAcceptInterceptors == 'undefined')
        shouldAcceptInterceptors = true;

      finalProperties.callsign.extension =
        biProperties.extension || app.BI.extension;

      finalProperties.callsign.src =
        biProperties.src || app.BI.defaultProperties.src;

      delete biProperties.extension;
      delete biProperties.src;

      if (!shouldAcceptInterceptors)
        delete biProperties[EVENT_INTERCEPTOR];

      finalProperties.props = biProperties;

      return finalProperties;
    }

    function prepareBIReport(biProperties, isLive) {
      var extension   = biProperties.callsign.extension,
          finalReport = '?src=' + biProperties.callsign.src,
          isCompleted = true;

      for (var property in biProperties.props) {
        if (biProperties.props.hasOwnProperty(property) && property != EVENT_INTERCEPTOR) {
          if (typeof biProperties.props[property] == 'function') {
            if (isLive) {
              var fnReturn = biProperties.props[property]();

              if (fnReturn) finalReport += '&' + property + '=' + fnReturn;
            } else {
              isCompleted = false;
              break;
            }
          } else {
            finalReport += '&' + property + '=' + biProperties.props[property];
          }
        }
      }

      return isCompleted && (extension + finalReport);
    }
	
    function prepareBIReportUrl(biReport) {
      if (biReport) {
        // console.log('prepareBIReportUrl', W.Site.Common.BI.path + biReport);
        return W.Site.Common.BI.path + biReport;
      } else {
        // console.log('Dynamic report detected, aborting');
        return biReport;
      }
    }

    function dynamicBIReport(finalProperties) {
      var biReport = prepareBIReport(finalProperties, LIVE_EVENT);

      reportBIEvent(prepareBIReportUrl(biReport));
    }

    function boot(fn, finalProperties, el, e) {
      // Clone the BI properties, so if developer should
      // change them through the interceptor, it won't persist
      var props = shallowClone(finalProperties.props);
      delete props[EVENT_INTERCEPTOR];

      fn.call(el, e, props, function completeReport() {
        var finalProperties = prepareBICallsign(props, false)

        dynamicBIReport(finalProperties);
      });
    }

    function bindBIEvent(el, type, biProperties) {
      var finalProperties = prepareBICallsign(biProperties),
          biReport,
          handler, fn;

      if (fn = finalProperties.props[EVENT_INTERCEPTOR]) {
        handler = boot.bind(null, fn, finalProperties, el);
      } else {
        biReport = prepareBIReport(finalProperties);

        if (biReport) {
          // If there are no live functions, cache this report
          handler = reportBIEvent.bind(null, prepareBIReportUrl(biReport));
        } else {
          handler = dynamicBIReport.bind(null, finalProperties);
        }
      }

      if (type == 'load') {
        if (el.tagName == 'HTML')
          el = window;
        else if (el.tagName == 'BODY') {
          onDomReady(handler);
          return;
        }
      }

      el.addEventListener(type, handler, false);
    }

    function reportBIEvent(biReportUrl) {
      var img = new Image();

      img.src = biReportUrl + '&_=' + new Date().getTime();
    }

    var biControls = getAllBIControls();

    forEach.call(biControls, function (biControl) {
      var attrs = biControl.attributes;

      for (var i in attrs) {
        if (attrs.hasOwnProperty(i) && attrs[i].name && attrs[i].name.indexOf(BI_PREFIX) === 0) {
          bindBIEvent(biControl, stripBI(attrs[i].name), parseBIProperyString(attrs[i].value));
        }
      }
    });
  }

  declarativeBI(W.App.name);

  var onDomReady = function (callback) {
    document.readyState == 'interactive' || document.readyState == 'complete' ? callback() : document.addEventListener('DOMContentLoaded', callback);
  };

  var sceneDuration = {
    value: 0
  };

  function setSceneDuration(duration) {
    sceneDuration.value = duration;
  }

  function updateSceneDuration() {
    var duration;

    // Duration formula:
    // <main> height - view port height
    // The Math.max... is to ensure we are not passing a negative value
    // to ScrollMagic, otherwise it throws an error and stops updating the duration
    duration = Math.max(0, main.clientHeight - window.innerHeight);

    setSceneDuration(duration);
  }

  onDomReady(function removeCloak() {
    main.classList.remove('wix-cloak');
  })

  onDomReady(function cacheHeaderHeight() {
    headerHeight = parseFloat(window.getComputedStyle(app).getPropertyValue('padding-top'));
  })



  function reportBI(evid, eventData) {
    var base = 'https://frog.wix.com/';

    eventData = eventData || {};
    eventData.evid = evid;
    var reportStr = "";
    for (var property in eventData) {
      reportStr  += '&' + property + '=' + encodeURIComponent(eventData[property]);
    }

    var img = new Image();
    img.src = base + 'spettatore_bi?_=' + new Date().getTime() + '&' + reportStr;
  }

  function sendDealerClickBI(targetUrl) {
    var metaSiteIdRegex = /metaSiteId=([\w-]+)/;
    var siteIdRegex = /siteId=([\w-]+)/;
    var esiRegex = /editorSessionId=([\w-]+)/;

    var eventData = W.App.Templates.BI.defaultProperties;
    eventData.metaSiteId = targetUrl.match(metaSiteIdRegex)[1];
    eventData.openingSiteID = targetUrl.match(siteIdRegex)[1];
    eventData.esi = targetUrl.match(esiRegex)[1];
    eventData.origin = 'dealerApi';

    reportBI(EDIT_TEMPLATE_EVENT_ID, eventData);
  }

 


  function initSceneDuration() {
    window.removeEventListener('load', initSceneDuration);

    var debouncedDurationUpdate = debounce(function () {
      updateSceneDuration();
    }, 250);

    // // Get initial value
    // setTimeout(function () {
    //   updateSceneDuration();
    // }, 0)

    window.removeEventListener('resize', debouncedDurationUpdate);
    window.addEventListener('resize', debouncedDurationUpdate, true);
  }

  window.addEventListener('load', initSceneDuration);

  function InfosScrolling() {
    var infos, thumbnails,
        tgContainer = document.querySelector('.tg-container'),
        debouncedInfosScrolbarsUpdate = debounce(updateInfosScrollbars, 250);

    function initInfosScrolling() {
      infos      = getInfos();
      thumbnails = getThumbnails();

      forEach.call(thumbnails, listenToImageLoad);
    }

    function listenToImageLoad(image, i) {
      if (image.complete) {
        onThumbnailLoad();
      } else {
        image.addEventListener('load', onThumbnailLoad, false);
      }

      function onThumbnailLoad() {
        // Thumbnails are hidden by default via CSS,
        // So, unhide this thumbnail after it's image has completely loaded
        // (avoids showing half-images while they are loading)
        showThumbnail(thumbnails[i].parentElement.parentElement);

        // Update scroll duration cache so sidebar will behave correctly ASAP
        // (Sticky behavior is dependent on gallery height which is affected by thumbnails)
        updateSceneDuration();
        // And update the sidebar accordingly so it doesn't break if a user is a scroll junky
        // and scrolls impatiently on load. This function is created asyncly so wait until it's defined
        // This all happens very quickly so it's ok if we miss out on a couple of hits
        typeof updateSidebar == 'function' && updateSidebar();

        if (isOptiscrollCompatible) initScrollbarFor(infos[i]);
      }
    }

    function showThumbnail(thumbnail) {
      thumbnail.classList.remove('-hidden');
    }

    function initScrollbarFor(info) {
      var infoBox = info.firstElementChild;

      // Add Optiscroll classes
      info.classList.add('optiscroll');
      infoBox.classList.add('optiscroll-content');

      // Ensure we'll get a scrollbar even if text is short
      // (Otherwise optiscroll will not init)
      if (info.clientHeight >= infoBox.scrollHeight)
        infoBox.style.overflowY = 'scroll';

      accessInfoScroller(info, function () {
        info.optiScroll = new Optiscroll(info, { preventParentScroll: true });
      });
    }

    function updateInfosScrollbars() {
      infos = getInfos();

      forEach.call(infos, resetInfoScrollbar);
    }

    function resetInfoScrollbar(info) {
      info.optiScroll && accessInfoScroller(info, function () {
        info.firstElementChild.style.width = null;
        info.optiScroll.update({ reset: true });
      });
    }

    function accessInfoScroller(info, fn) {
      info.classList.add('-visible');
      fn();
      info.classList.remove('-visible');
    }

    function listenToSidebarToggles(e) {
      if (e.target == tgContainer) updateInfosScrollbars();
    }

    if (isOptiscrollCompatible) {
      window.removeEventListener('resize', debouncedInfosScrolbarsUpdate);
      window.addEventListener('resize', debouncedInfosScrolbarsUpdate, true);

      tgContainer.removeEventListener('transitionend', listenToSidebarToggles);
      tgContainer.addEventListener('transitionend', listenToSidebarToggles, false);
    }

    return {
      init: initInfosScrolling,
      update: updateInfosScrollbars,
      accessInfoScroller: accessInfoScroller
    }
  }

  var infosScrolling = InfosScrolling();

  onDomReady(infosScrolling.init);

  function initThumbnailHovers() {
    var gallery    = document.querySelector('.tg-gallery'),
        thumbnails = getThumbnails(),
        hovers     = getHovers();

    thumbnails = map.call(thumbnails, function (thumbnail) {
      return thumbnail.parentElement;
    });

    forEach.call(thumbnails, function (thumbnail, i) {
      thumbnail.addEventListener('mouseleave', hideHover, false);
      hovers[i].addEventListener('mouseleave', hideHover, false);
      hovers[i].addEventListener('mouseenter', watchHoverOpacity, false);

      hovers[i].removeEventListener('transitionend', watchHoverOpacity);
      hovers[i].addEventListener('transitionend', watchHoverOpacity, false);
    });

    function watchHoverOpacity(e) {
      var opacity = window.getComputedStyle(this).getPropertyValue('opacity');

      if (opacity == 1) {
        this.classList.add('-active');
      } else {
        this.classList.remove('-visible');
      }
    }

    function hideHover(e) {
      var hover = getTgHover(this);

      if (hover) {
        // Make sure to hide the hover again after animation is finished.
        // Also takes care of when the mouse was moved out of the thumbnail
        // before the animation even started
        hover.classList.add('-visible');
      }
    }

    function getTgHover(el) {
      if (el.classList.contains('tg-thumbnail')) {
        return el.children[2];
      } else if (el.classList.contains('tg-hover')) {
        return el;
      }

      return false;
    }
  }

  onDomReady(initThumbnailHovers);

  function initInfoHovers() {
    var infoSwitches = getInfoSwitches(),
        infos        = getInfos(),
        thumbnails   = getThumbnails();

    // Reset (hide) info hover after mouseout
    forEach.call(thumbnails, function (image, i) {
      image.parentElement.addEventListener('mouseleave', function () {
        function hideInfoHover() {
          // Hide the info hover
          infoSwitches[i].checked = false;

          // Reset its scrollbar
          infos[i].optiScroll &&
            infosScrolling.accessInfoScroller(infos[i], function () {
              infos[i].optiScroll.scrollTo(false, 'top', 0);
              infos[i].optiScroll.update();
            });

          image.parentElement.removeEventListener('transitionend', hideInfoHover);
        }

        image.parentElement.addEventListener('transitionend', hideInfoHover, false);
      });
    });
  }

  onDomReady(initInfoHovers);

  function initSidebarSwitch() {
    var sidebar, sbSwitch, sbToggle, sbCollapse, sbSwitchStroke;

    function init() {
      sidebar        = document.getElementById('sidebar');
      sbSwitch       = document.getElementById('sb-switch');
      sbToggle       = document.getElementById('sb-toggle');
      sbCollapse     = document.getElementById('sb-collapse');
      sbSwitchStroke = document.querySelector('.sbs-stroke');

      sidebar.removeEventListener('transitionend', sidebarAnimationEnded);
      sidebar.addEventListener('transitionend', sidebarAnimationEnded, false);

      sbSwitchStroke.removeEventListener('transitionend', toggleAnimationEnded);
      sbSwitchStroke.addEventListener('transitionend', toggleAnimationEnded, false);

      sbSwitch.addEventListener('click', function () {
        sidebar.classList.add('-transitioning');

        if (this.checked) {
          showSidebar();
          // cookie.set("sidebar-state", main.classList.contains('-sidebar-open'), Infinity);
        } else {
          hideSidebar();
          // cookie.set("sidebar-state", main.classList.contains('-sidebar-open'), Infinity);
        }
      });

      sbCollapse.addEventListener('click', function () {
        sbSwitch.click();
      });

      app.addEventListener('scroll', trackSBSwitch, false);
    }

    function showSidebar() {
      sbToggle.classList.add('-transitioning');

      main.classList.add('-sidebar-open');
      main.classList.remove('-sidebar-closed');

      getScrollTop() > headerHeight && scrollBodyToTop(true);
      trackSBSwitch(true);
    }

    function hideSidebar() {
      // Animate button
      sbSwitch.classList.remove('-hidden');
      sbToggle.classList.add('-transitioning');
      sbCollapse.classList.add('-hidden');

      // Animate sidebar
      main.classList.add('-sidebar-closed');
      main.classList.remove('-sidebar-open');

      // Update switch state
      trackSBSwitch(true);
    }

    function sidebarAnimationEnded() {
      sidebar.classList.remove('-transitioning');

      // The sidebar animation changes the height of the page,
      // so we should update the scene length for ScrollMagic
      // accordingly. (Instead of ScrollMagic polling the DOM constantly)
      updateSceneDuration();

      // Update switch state
      trackSBSwitch(true);
    }

    function toggleAnimationEnded() {
      sbToggle.classList.remove('-transitioning');

      if (sbSwitch.checked) {
        sbCollapse.classList.remove('-hidden');
        sbSwitch.classList.add('-hidden');
      }

      // Update switch state
      // trackSBSwitch(true);
    }

    function trackSBSwitch(force) {
      if (force !== true && sbSwitch.checked) {
        return;
      }

      var scrollTop      = getScrollTop(),
          mainHeight     = main.scrollHeight,
          bottomBoundary = 350,
          sbSwitchHeight = 65;

      if (scrollTop >= headerHeight) {
        if (scrollTop + sbSwitchHeight < mainHeight + headerHeight - bottomBoundary) {
          sbSwitch.classList.remove('-to-bottom');
          sbToggle.classList.remove('-to-bottom');
          sbSwitch.classList.add('-fixed');
          sbToggle.classList.add('-fixed');
        } else {
          sbSwitch.classList.add('-to-bottom');
          sbToggle.classList.add('-to-bottom');
        }
      } else {
        sbSwitch.classList.remove('-to-bottom');
        sbToggle.classList.remove('-to-bottom');
        sbSwitch.classList.remove('-fixed');
        sbToggle.classList.remove('-fixed');
      }
    }

    return {
      init: init,
      track: trackSBSwitch
    }
  }

  var sbSwitch = initSidebarSwitch();
  onDomReady(sbSwitch.init);

  isOptiscrollCompatible && onDomReady(function initPageScrollbar() {
    document.documentElement.classList.add('using-optiscroll');
    document.body.classList.add('optiscroll');
    app.classList.add('optiscroll-content');
    var optiScroll = new Optiscroll(document.body);
  });

  onDomReady(function initSidebarStickyIsolatedScrolling() {
    //----------------------------------*\
    // STICKY SCROLLING
    //----------------------------------*/

    // Keep some styles for future reference
    var sidebar      = document.getElementById('sidebar'),
        tgContainer  = document.getElementById('gallery-container'),
        mainBottom   = parseFloat(window.getComputedStyle(tgContainer).getPropertyValue('padding-bottom')),
        sidebarWidth = parseFloat(window.getComputedStyle(sidebar).getPropertyValue('width'));

    var wrapper, fragment, scrollbar;

    var mode = 1;

    var matches = sidebar.matches ? 'matches' : 'msMatchesSelector';

    // Wrap sidebar in wrapper so we can
    // use it to make the sidebar sticky inside it
    wrapper = document.createElement('div');
    wrapper.className = 'sb-wrapper';
    main.insertBefore(wrapper, tgContainer.previousElementSibling);

    fragment = document.createDocumentFragment();
    fragment.appendChild(sidebar);

    wrapper.appendChild(fragment);

    // Track sidebar while scrolling
    (isOptiscrollCompatible ? app : window).addEventListener('scroll', trackSidebar, false);

    updateSidebar = trackSidebar;
    function trackSidebar() {
      var scrollTop     = getScrollTop(),
          mainHeight    = main.scrollHeight,
          scrollBottom  = scrollTop + window.innerHeight;

      if (scrollTop < headerHeight) {
        mode !== 1 && resetSidebarToTop();
      } else {
        if (scrollBottom < mainHeight + headerHeight) {
          mode !== 2 && fixSidebar();
        } else {
          mode !== 3 && alignSidebarWithFooter();
        }
      }
    }

    function resetSidebarToTop() {
      mode = 1;

      sidebar.classList.remove('-to-bottom');
      sidebar.classList.remove('-fixed');
      sidebar.style.top = '1px';

      if (isOptiscrollCompatible) {
        scrollbar.style.removeProperty('top');
        scrollbar.classList.remove('-visible');
      }

      // console.log('top');
    }

    function fixSidebar() {
      mode = 2;

      sidebar.classList.remove('-to-bottom');
      sidebar.classList.add('-fixed');
      sidebar.style.top = '0px';

      if (isOptiscrollCompatible) {
        scrollbar.style.removeProperty('top');
        if (sidebar[matches](':hover')) {
          scrollbar.classList.add('-visible');
        }
      }

      // console.log('fixed');
    }

    function alignSidebarWithFooter() {
      mode = 3;

      sidebar.classList.add('-to-bottom');
      sidebar.style.top = sceneDuration.value - 1 + 'px';

      if (isOptiscrollCompatible) {
        scrollbar.style.top = sceneDuration.value - 1 + 'px';
      }

      // console.log('bottom');
    }

    //----------------------------------*\
    // ISOLATED SCROLLING
    //----------------------------------*/

    for (var i = wheelEvents.length; i;) {
      sidebar.addEventListener(wheelEvents[--i], function (e) {
        var delta     = e.deltaY * -1,
            scrollTop = getScrollTop();

        // console.group()
        // console.log('delta:', delta)
        // console.log('scrollTop:', scrollTop)
        // console.log('sidebar, scrollTop:', sidebar.scrollTop, 'scrollHeight:', sidebar.scrollHeight, 'clientHeight:', sidebar.clientHeight);
        // console.log('wrapper, offsetTop:', sidebar.parentElement.offsetTop);
        // console.groupEnd();

        if (delta < 0
            && sidebar.classList.contains('-fixed')
            && !sidebar.classList.contains('-to-bottom')
            && sidebar.scrollTop == (sidebar.scrollHeight - sidebar.clientHeight)
            && scrollTop > sidebar.parentElement.offsetTop) {
          e.preventDefault();
        }
      }, false);
    }

    //----------------------------------*\
    // OPTISCROLLER
    //----------------------------------*/

    isOptiscrollCompatible && initSidebarOptiscroller();

    function initSidebarOptiscroller() {
      sidebar.classList.add('optiscroll-content');
      wrapper.classList.add('optiscroll');

      var optiScroll = new Optiscroll(wrapper);
      scrollbar = optiScroll.scrollbars.v.element();
    }
  });

  function scrollBodyToTop(shouldAnimate) {
    if (shouldAnimate) {
      animateToTop();
    } else {
      setScrollTop(0);
    }
  }

  function animateToTop() {
    var scrollTop = getScrollTop(),
        step = scrollTop / 12,
        time = Math.round(30 / scrollTop) + 10;

    setScrollTop(scrollTop - step);
    sbSwitch.track(true);

    if (scrollTop > 0) {
      setTimeout(animateToTop, time);
    }
  }

  function focusOnActiveCategory() {
    function focus(force) {
      window.removeEventListener('load', focus);

      var sidebar = document.getElementById('sidebar');

      var active1stLevelCategory = document.querySelectorAll('.sb-lvl-1-cat.-active')[0],
          active2ndLevelCategory = document.querySelectorAll('.sb-lvl-2-cat.-active')[0],
          willScrollBody = force === true,
          activeCategory = active1stLevelCategory || active2ndLevelCategory || false,
          isAboveTheFold = activeCategory
                           && isCategoryInViewport(activeCategory.children[1] || activeCategory.firstElementChild);

      if (activeCategory && (willScrollBody || !isAboveTheFold)) {
        if (!willScrollBody) {
          // console.log('Will scroll to header height');
          setScrollTop(headerHeight);
        }

        var acTopOffset = offsetFrom(activeCategory, document.body),
            scrollDiff  = (acTopOffset - sidebar.clientHeight / 2)  + headerHeight + 21, // (+ header height, + half of category height),
            scrollLimit = sidebar.scrollHeight - sidebar.clientHeight;

        window.setTimeout(function () {
          // console.log('Will scroll to top and focus on category');
          sidebar.scrollTop = scrollLimit > 0 ? Math.min(scrollDiff, scrollLimit) : scrollDiff;
          willScrollBody && getScrollTop() > 0 && scrollBodyToTop();
        }, 1);
      }
    }

    function offsetFrom(el, target) {
      var targetRect = target.getBoundingClientRect(),
          sourceRect = el.getBoundingClientRect(),
          offsetTop  = sourceRect.top - targetRect.top;

      return offsetTop;
    }

    function isCategoryInViewport(el) {
      var rect = el.getBoundingClientRect();

      return rect.top + sidebar.scrollTop    >= 0 &&
             rect.bottom + sidebar.scrollTop <= (window.innerHeight || document.documentElement.clientHeight);
    }

    return {
      focus: focus
    }
  };
  
  ///////////////////////
  
  var themeList = [];
  var nextPage = null;

  ///////////////////////////////// Banner /////////////////////////////////////

  var themeSwiper = new Swiper ('#themes-swiper .swiper-container', {
    // autoplay: 5000,
    // loop: true,
    // speed: 500,
    // effect: 'fade',
    // nextButton: '.theme__swiper-button.next',
    // prevButton: '.theme__swiper-button.prev'
  });

  $('#themes-swiper').on('click', '.js-themes-readmore', function() {
    var h = app.offset().top;
    $('body').animate({
        scrollTop: h
    }, 700);

  });

  var currentScroll = 0
  $(window).on('scroll', _.throttle(function(){
    var nextScroll = $(this).scrollTop();
    if(nextScroll > currentScroll){
      $('#mobileDropUp').addClass('appear');
    } else {
      $('#mobileDropUp').removeClass('appear');
      $('#mobileDropUp').removeClass('open');
    }
  }, 200));

  //////////////////////////// Theme List Methods //////////////////////////////

  

  function renderThemeList(response) {
    nextPage = response.next;
    $("#theme-list__main-content .js-list__fetch-more").removeClass('disabled');
    if(response.next) {
      $("#theme-list__main-content .js-list__fetch-more.hidden").removeClass('hidden');
    }else {
      $("#theme-list__main-content .js-list__fetch-more").addClass('hidden');
    }

    themeList = themeList.concat(response.results);
    var themes_html = nunjucks.renderString($('#template-theme-list-item').html(), {themes: themeList});
    $('#theme-list__main-content .theme-list').html(themes_html);
    HeyShop.fillImages($('#theme-list__main-content .theme-list'));
  }
  
})(window, document);
